#include "disk.h"

#include<stdio.h>
#include<unistd.h>
#include <regex>

bool device::pcall(const std::string &cmd, std::function<bool(const std::string &line)> callback, int max_line_len) {
  if (cmd.empty())
    return false;
  if (callback && max_line_len <= 0)
    return false;

  FILE *procpt = ::popen(cmd.c_str(), "r");
  if (!procpt)
    return false;

  if (callback) {
    char buf[256];
    
    while (fgets(buf, max_line_len, procpt)) {
      if (!callback(buf))
        break;
    }
  }

  ::pclose(procpt);
  return true;
}

bool device::pscan(const std::string &cmd, const std::string &begin, const std::string &end
  , std::function<bool(const std::string &line, escan_stat stat)> callback) {
  if (!callback) return false;
  if (cmd.empty() || begin.empty() || end.empty())
    return false;

  std::regex bregex(begin);
  std::regex eregex(end);
  bool bstart = false;

  return pcall(cmd, [&callback, &bstart, &bregex, &eregex](const std::string &line) -> bool {
    if (bstart) {
      if (std::regex_match(line, eregex)) {
        bstart = false;
        if (!callback(line, escan_stat::end)) {
          bstart = false;
        }
        return true;
      }
      if (!callback(line, escan_stat::runing)) {
        bstart = false;
      }
    } else {
      if (std::regex_match(line, bregex)) {
        bstart = true;
        if (!callback(line, escan_stat::start)) {
          bstart = false;
        }
      }
    }
    return true;
  });  
}

std::string device::trim(const std::string &str, const std::string &flag) {
  int b = str.find_first_not_of(flag);
  int e = str.find_last_not_of(flag);
  return std::move(str.substr(b, e - b + 1));
}

bool device::scan_disk(std::set<std::string> &disks) {
  return pcall("fdisk -l", [&disks](const std::string &line) -> bool {
    static std::regex regex(R"(^\s*Disk\s*(/dev/.*):.*\n$)");
    std::smatch match;

    if (std::regex_match(line, match, regex) && match.size() > 1) {
      disks.insert(match[1].str());
		}

    return true;
  });
}
bool device::disk_info(const std::string &dev, sdisk_info *si) {
  if (!si || dev.empty())
    return false;

  return true;
}

bool device::scan_memory(std::set<std::string> &info) {
  std::string msg;
  return pscan("dmidecode -q -t memory"
    , R"(^\s*Memory Device\n$)"
    , R"(^\s*\n$)"
    , [&info, &msg](const std::string &line, escan_stat stat) -> bool {
      if (stat == escan_stat::runing) {
        auto pos = line.find("Size: No Module Installed");
        if (pos != std::string::npos)
          return false;
        msg.append(trim(line, "\t"));
      } else if (stat == escan_stat::start) {
        msg.clear();
      } else if (stat == escan_stat::end) {
        if (!msg.empty())
          info.insert(msg);
      }

      return true;
    });
}

bool device::scan_bios(std::set<std::string> &info) {
  std::string msg;
  return pscan("dmidecode -q -t bios"
    , R"(^\s*BIOS Information\n$)"
    , R"(^\s*\n$)"
    , [&info, &msg](const std::string &line, escan_stat stat) -> bool {
      if (stat == escan_stat::runing) {
        msg.append(trim(line, "\t"));
      } else if (stat == escan_stat::start) {
        msg.clear();
      } else if (stat == escan_stat::end) {
        if (!msg.empty())
          info.insert(msg);
      }

      return true;
    });
}

bool device::scan_board(std::set<std::string> &info) {
  std::string msg;
  return pscan("dmidecode -q -t baseboard"
    , R"(^\s*Base Board Information\n$)"
    , R"(^\s*\n$)"
    , [&info, &msg](const std::string &line, escan_stat stat) -> bool {
      if (stat == escan_stat::runing) {
        msg.append(trim(line, "\t"));
      } else if (stat == escan_stat::start) {
        msg.clear();
      } else if (stat == escan_stat::end) {
        if (!msg.empty())
          info.insert(msg);
      }

      return true;
    });
}

bool device::scan_system(std::set<std::string> &info) {
  std::string msg;
  return pscan("dmidecode -q -t system"
    , R"(^\s*System Information\n$)"
    , R"(^\s*\n$)"
    , [&info, &msg](const std::string &line, escan_stat stat) -> bool {
      if (stat == escan_stat::runing) {
        msg.append(trim(line, "\t"));
      } else if (stat == escan_stat::start) {
        msg.clear();
      } else if (stat == escan_stat::end) {
        if (!msg.empty())
          info.insert(msg);
      }

      return true;
    });
}

bool device::scan_chassis(std::set<std::string> &info) {
  std::string msg;
  return pscan("dmidecode -q -t chassis"
    , R"(^\s*Chassis Information\n$)"
    , R"(^\s*\n$)"
    , [&info, &msg](const std::string &line, escan_stat stat) -> bool {
      if (stat == escan_stat::runing) {
        msg.append(trim(line, "\t"));
      } else if (stat == escan_stat::start) {
        msg.clear();
      } else if (stat == escan_stat::end) {
        if (!msg.empty())
          info.insert(msg);
      }

      return true;
    });
}

bool device::scan_connector(std::set<std::string> &info) {
  std::string msg;
  return pscan("dmidecode -q -t connector"
    , R"(^\s*Port Connector Information\n$)"
    , R"(^\s*\n$)"
    , [&info, &msg](const std::string &line, escan_stat stat) -> bool {
      if (stat == escan_stat::runing) {
        msg.append(trim(line, "\t"));
      } else if (stat == escan_stat::start) {
        msg.clear();
      } else if (stat == escan_stat::end) {
        if (!msg.empty())
          info.insert(msg);
      }

      return true;
    });
}

//------------------------//
bool device::scan_diaplay(std::set<std::string> &info) {
  std::string msg;
  bool bok = pscan("lshw -C display"
    , R"(^\s*\*-display\s*\n$)"
    , R"(^\s*\n$)"
    , [&info, &msg](const std::string &line, escan_stat stat) -> bool {
      if (stat == escan_stat::runing) {
        msg.append(trim(line, " "));
      } else if (stat == escan_stat::start) {
        msg.clear();
      } else if (stat == escan_stat::end) {
        if (!msg.empty()) {
          info.insert(msg);
          msg.clear();
        }
      }

      return true;
    });
  if (!msg.empty())
    info.insert(msg);
  return bok;
}

bool device::scan_network(std::set<std::string> &info) {
  std::string msg;
  bool bok = pscan("lshw -C network"
    , R"(^\s*\*-network\s*\n$)"
    , R"(^\s*\n$)"
    , [&info, &msg](const std::string &line, escan_stat stat) -> bool {
      if (stat == escan_stat::runing) {
        msg.append(trim(line, " "));
      } else if (stat == escan_stat::start) {
        msg.clear();
      } else if (stat == escan_stat::end) {
        if (!msg.empty()) {
          info.insert(msg);
          msg.clear();
        }
      }

      return true;
    });
  if (!msg.empty())
    info.insert(msg);
  return bok;
}